package edu.ucsb.cs176b.android;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;

import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.AlertDialog;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

public class MainMenu extends Activity implements LocationAware {
    
	private LocationManager locationManager;
	private LocationListener locationListener;
	private Location userLocation;
	
	private Spinner treasureChooser;
	private ArrayAdapter<Integer> spinnerAdapter;
	
	private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 1337;
	
	private String username;
	private String token;
	private String host;
	private String returnUrl;
	
	public static final String PREFS_NAME = "MyPrefsFile";

	private String statusText;
	private String infoText;
	private String toastText;
	private String alertMessage;
	
	private int numberOfTreasures;
	private int chosenTreasure;
	
	
	// UI elements
	private Button gameOn;
	private Button showMap;
	private Button takePicture;
	private Button checkIn;
	private Button cancelCheckIn;
	private TextView status;
	private TextView info;
	

	public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
        
        // Init
        numberOfTreasures = 1;
        chosenTreasure = 1;
        
        status = (TextView) findViewById(R.id.game_status);
        info = (TextView) findViewById(R.id.info_3);

        
        // Get data from intent
        if(getIntent().getExtras().containsKey("username")) {
        	System.out.println("getextra contains username");
	        username= getIntent().getExtras().getString("username");
	        host = getIntent().getExtras().getString("host");
	        token = getIntent().getExtras().getString("token");
	     
	        // Saves the temporary userdata as new intent will be created from C2DMReciever when GAMEON gets recieved
	        SharedPreferences.Editor editor = settings.edit();
	        editor.putString("username", username);
	        editor.putString("host", host);
	        editor.putString("token", token);
	        editor.commit();
	        
        }
        
        if(getIntent().getExtras().containsKey("started")) {
        	username = settings.getString("username", "usern");
    	    host = settings.getString("host", "usern");
    	    token = settings.getString("token", "usern");
        }      

        // DEBUG
        //System.out.println("Game started: " + gameon);
        //System.out.println("Username in MainMenu: " + username);
        //System.out.println("Token in MainMenu: " + token);
 
        // Acquire a reference to the system Location Manager
        locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
        // get the latest known location from network (fastest)
        userLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
        // Create a location listener
        locationListener = new MyLocationListener(this);
        
        
        // Create "Choose treasure ID" spinner
        treasureChooser = (Spinner)findViewById(R.id.spinner);
        spinnerAdapter = new ArrayAdapter<Integer>(this, android.R.layout.simple_spinner_item);
        spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        treasureChooser.setAdapter(spinnerAdapter);
        
        setStatus("NOT PLAYING");
        setInfo( "Welcome to the treasure hunt game!\nYou have to wait for a GAME ON-meassage "+
        		"from the server, or you can try starting manually by pushing the button above.");
        
        // GAME ON button
        gameOn = (Button)findViewById(R.id.game_on);
        gameOn.setOnClickListener(new OnClickListener() {
        	public void onClick(View v) {
        		gameOn();
        	} 
        });
        
        // "Show map" button
        showMap = (Button)findViewById(R.id.show_map);
        showMap.setOnClickListener(new OnClickListener() {
        	public void onClick(View v) {
        		showMap();
        	} 
        });
        
        // "Checkin" button
        checkIn = (Button)findViewById(R.id.check_in);
        checkIn.setOnClickListener(new OnClickListener() {
        	public void onClick(View v) {
        		Toast.makeText(getApplicationContext(), "Checking in...", Toast.LENGTH_LONG).show();	
        		// Starting in background thread since it involves network connections
        		new Thread(new Runnable() {
					public void run() {
						checkIn();	
					}
				}).start();
        	} 
        });
        
        // "Cancel checkin" button
        cancelCheckIn = (Button)findViewById(R.id.cancel_check_in);
        cancelCheckIn.setOnClickListener(new OnClickListener() {
        	public void onClick(View v) {
        		Toast.makeText(getApplicationContext(), "Cancels checkin...", Toast.LENGTH_LONG).show();	
        		// Starting in background thread since it involves network connections
        		new Thread(new Runnable() {
					public void run() {
						cancelCheckIn();	
					}
				}).start();
        	} 
        });
        
        // "Take picture" button
        takePicture = (Button)findViewById(R.id.take_picture);
        takePicture.setOnClickListener(new OnClickListener() {
        	public void onClick(View v) {
        		takePicture();
        	}
        });
    }
	
	/** Register for the updates when Activity is in foreground */
	@Override
	protected void onResume() {
		super.onResume();
		
		// Mediator used by C2DM
		MenuMediator.setMenuMediator(this);
		
		// Register the listener with the Location Manager to receive location updates (every 10th second)
		locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 10000, 0, locationListener);
		locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 0, locationListener);
	}

	/** Stop the updates when Activity is paused */
	@Override
	protected void onPause() {
		super.onPause();
		// Stop location updates
		locationManager.removeUpdates(locationListener);
	}
	
	public void gameOn(){
		runOnUiThread(new Runnable() {
				public void run() {
					gameOn.setVisibility(View.GONE);
					cancelCheckIn.setVisibility(View.GONE);
					takePicture.setVisibility(View.GONE);
					
					new Thread(new Runnable() {
						public void run() {
							getNubmerOfTreasures();	
						}
					}).start();
				}
			});
	}
	
	public void gameOff(){
		runOnUiThread(new Runnable() {
			public void run() {
				findViewById(R.id.info_1).setVisibility(View.GONE);
				findViewById(R.id.info_2).setVisibility(View.GONE);
				showMap.setVisibility(View.GONE);
				checkIn.setVisibility(View.GONE);
				takePicture.setVisibility(View.GONE);
				
				gameOn.setVisibility(View.VISIBLE);
				
				setStatus("NOT PLAYING");
		        setInfo( "Welcome to the treasure hunt game!\nYou have to wait for a GAME ON-meassage "+
		        		"from the server, or you can try starting manually by pushing the button above.");
			}
		});
	}

	private void getNubmerOfTreasures(){
		// Network connection...
		try {
			String strResponse = NetworkUtil.getSringFromUrl("http://"+ host +"/get_n?token=" + token );
			Log.d("MainMenu", "Number of treasures response: " + strResponse);
			
			if(strResponse==null){
				gameOff();
				return;
			}
			
			JSONObject json = new JSONObject(strResponse);
			JSONObject status = json.getJSONObject("status");
				
			// Handle response code
			switch (status.getInt("code")) {
				case 0:
					JSONObject jsonResponse = json.getJSONObject("response");
					numberOfTreasures = jsonResponse.getInt("n");
					break;
				default:
					gameOff();
					break;
			}
					
		} catch (JSONException e) {
			e.printStackTrace();
		}
		
		runOnUiThread(new Runnable() {
			public void run() {
				createSpinner();
				findViewById(R.id.info_1).setVisibility(View.VISIBLE);
				findViewById(R.id.info_2).setVisibility(View.VISIBLE);
				showMap.setVisibility(View.VISIBLE);
				checkIn.setVisibility(View.VISIBLE);
				
				status.setText("Game status: PLAYING (ready to check in)");
				info.setText("Let's find those treasures!");
			}
		});
	}
	
	private void checkIn(){
		if(userLocation==null){
			showToast( "Please wait for application to find your position!" );	
			return;
		}
		// Network connection...
		try {
			String strResponse = NetworkUtil.getSringFromUrl("http://"+ host +"/checkin?token=" + token + "&latitude=" + userLocation.getLatitude() + "&longitude=" + userLocation.getLongitude() +  "&cid=" + chosenTreasure );
			Log.d("MainMenu", "Checkin response: " + strResponse);
			
			// strResponse will be null if we had an network error
			if(strResponse==null){
				showToast("Could not connect to the network!");
				gameOff();
				return;
			}
			
			JSONObject json = new JSONObject(strResponse);
			JSONObject status = json.getJSONObject("status");
			
			// Handle response code
			switch (status.getInt("code")) {
				case 0:
					goToCheckedInState();
					setInfo("Hint: " + json.getJSONObject("response").getString("hint") );
					break;
				case 10:
					showToast( "You are too far from checkinpoint!" );
					break;
				case 12:
					showToast( "You are already checked in!" );	
					goToCheckedInState();
					break;
				case 20:
					gameOff();
					break;
				default:
					showToast( "Something went wrong. Please try again!\n(Maybe you have to login again..)" );	
					break;
			}
					
		} catch (JSONException e) {
			e.printStackTrace();
		}
	}
	
	private void cancelCheckIn(){
		// Network connection...
		try {
			String strResponse = NetworkUtil.getSringFromUrl("http://"+ host +"/cancel_checkin?token=" + token );
			Log.d("MainMenu", "Cancel checkin response: " + strResponse);
			
			// strResponse will be null if we had an network error
			if(strResponse==null){
				gameOff();
				return;
			}
			
			JSONObject json = new JSONObject(strResponse);
			JSONObject status = json.getJSONObject("status");
			
			// Handle response code
			switch (status.getInt("code")) {
				case 0:
					goToGameOnState();
					setInfo("Chekin cancelled.. Let's look for another treasure :)");
					break;
				case 11:
				case 13:	
					showToast( "You are not checked in anywhere!" );	
					goToGameOnState();
					break;
				case 20:
					gameOff();
					break;
				default:
					showToast( "Something went wrong. Please try again!\n(Maybe you have to login again..)" );	
					break;
			}
			
		} catch (JSONException e) {
			e.printStackTrace();
		}
	}
	
	private void goToCheckedInState(){
		runOnUiThread(new Runnable() {
			public void run() {
				findViewById(R.id.info_1).setVisibility(View.VISIBLE);
				findViewById(R.id.info_2).setVisibility(View.VISIBLE);
				showMap.setVisibility(View.VISIBLE);
				cancelCheckIn.setVisibility(View.VISIBLE);
				takePicture.setVisibility(View.VISIBLE);
				
				gameOn.setVisibility(View.GONE);
				checkIn.setVisibility(View.GONE);
				
				status.setText("Game status: PLAYING (checked in)");
			}
		});	
	}
	
	private void goToGameOnState(){
		runOnUiThread(new Runnable() {
			public void run() {
				findViewById(R.id.info_1).setVisibility(View.VISIBLE);
				findViewById(R.id.info_2).setVisibility(View.VISIBLE);
				showMap.setVisibility(View.VISIBLE);
				checkIn.setVisibility(View.VISIBLE);
				
				gameOn.setVisibility(View.GONE);
				cancelCheckIn.setVisibility(View.GONE);
				takePicture.setVisibility(View.GONE);
				
				status.setText("Game status: PLAYING (ready to check in)");
				info.setText("Ready to checkin :)");
			}
		});	
	}
	
	public void showAlert(String msg){
		alertMessage = msg;
		runOnUiThread(new Runnable() {
			public void run() {
				// Linkify the message
			    final SpannableString s = new SpannableString(alertMessage);
			    Linkify.addLinks(s, Linkify.ALL);

			    final AlertDialog d = new AlertDialog.Builder(MainMenu.this)
			        .setPositiveButton("OK", null)
			        .setIcon(R.drawable.icon)
			        .setMessage( s )
			        .create();

			    d.show();

			    // Make the textview clickable. Must be called after show()
			    ((TextView)d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
			}
		});
	}
	
	void showToast(String text){
		toastText = text;
		runOnUiThread(new Runnable() { 
			public void run() {
				Toast.makeText(getApplicationContext(), toastText, Toast.LENGTH_SHORT).show();
			}
		});
	}
	
	private void setInfo(String text){
		infoText = text;
		runOnUiThread(new Runnable() { 
			public void run() {
				info.setText( infoText );
			}
		});
	}
	
	private void setStatus(String text){
		statusText = text;
		runOnUiThread(new Runnable() { 
			public void run() {
				status.setText( "Game status: " + statusText );
			}
		});
	}
	
	private void createSpinner(){
		spinnerAdapter.clear();
        for (int i = 0; i < numberOfTreasures; i++) {
        	spinnerAdapter.add(i+1);
		}
        treasureChooser.setOnItemSelectedListener(new OnItemSelectedListener() {
        	    public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
        	    	chosenTreasure = Integer.parseInt( parent.getItemAtPosition(pos).toString() );
        	    	Toast.makeText(getApplicationContext(), "Chosen treasure: " + chosenTreasure, Toast.LENGTH_SHORT).show();
        	    }
        	    public void onNothingSelected(AdapterView<?> parent) {
        	      // Do nothing.
        	    }
		});
        treasureChooser.setVisibility(View.VISIBLE);
	}
	
	public void showMap(){
		Intent myIntent = new Intent(getApplicationContext(), MyMap.class);
		myIntent.putExtra("numberOfTreasures", numberOfTreasures);
		myIntent.putExtra("chosenTreasure",   chosenTreasure);
		myIntent.putExtra("host", host);
		myIntent.putExtra("token", token);
		startActivity(myIntent);
	}
	
	private void takePicture(){
		// Saves settings
		SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
        SharedPreferences.Editor editor = settings.edit();
        editor.putInt("chosenTreasure", chosenTreasure);
        editor.commit();
        
		Intent myIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
		myIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment.getExternalStorageDirectory(),"th.jpg")));
		startActivityForResult(myIntent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
	}
	
	public boolean isOnline() {
		 ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
		 return cm.getActiveNetworkInfo().isConnectedOrConnecting();
	}
	
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	    if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
	    	if (resultCode == RESULT_OK) {		
	    		
	    		// Restore settings after activity has returned
	    		SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
	    		chosenTreasure = settings.getInt("chosenTreasure", 2);
	    		
	    		// Since this involves network communication we start it in background thread
	    		new Thread(new Runnable() {
					public void run() {
						uploadImage();
					}
				}).start();
	    	}
	    	
	    	
	    }

	}
	
	private void uploadImage(){
		goToGameOnState();
		// Finds the image on the disk and creates a byte[] of it
		byte [] bMapArray = null;
		try {
			String imagepath = Environment.getExternalStorageDirectory().getPath().toString()+"/th.jpg";
			FileInputStream fis = new FileInputStream(imagepath);
			BufferedInputStream buf = new BufferedInputStream(fis);
            bMapArray= new byte[buf.available()];
            buf.read(bMapArray);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// Code to verify response from server
		returnUrl = "";
		try {
			returnUrl = NetworkUtil.uploadPicture(host, bMapArray, token, chosenTreasure);
			JSONObject json = new JSONObject(returnUrl);
			JSONObject status = json.getJSONObject("status");
			int statuscode = status.getInt("code");
			if(status.getInt("code")==0){
				showToast( "Picture upload successful!");
			}
			else {
				showToast( "Picture upload failed: " + statuscode );
				goToCheckedInState();
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		} catch (JSONException e) {
			showToast( "Server error.. (TJ fucked up!)");
			goToGameOnState();
			e.printStackTrace();
		}
		
		// DEBUG
		//showToast( "DEBUG (upload picture response): " + returnUrl );
	}
	
	// Set userLocation, called from the locationListener when location is updated
	public void setLocation(Location l){
		userLocation = l;
	}
	
}